{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Chinese_htlData_Classification_with_BERT.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/ZeroLeon/Chinese_htlData_Classification_with_BERT/blob/master/Chinese_htlData_Classification_with_BERT.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XqLEbsvyOjJ5",
        "colab_type": "text"
      },
      "source": [
        "#在Fastai框架下基于BERT模型的中文文本分类\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KdRDycJLO1qW",
        "colab_type": "text"
      },
      "source": [
        "##Preparation"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Yea4tylQORLi",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#安装pytorch下包含BERT预训练模型的库\n",
        "!pip install pytorch-pretrained-bert"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "hmikQXQAPenZ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#引入必要的依赖包\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "\n",
        "from pathlib import Path\n",
        "from typing import *\n",
        "\n",
        "import torch\n",
        "import torch.optim as optim\n",
        "\n",
        "from fastai import *\n",
        "from fastai.vision import *\n",
        "from fastai.text import *\n",
        "from fastai.callbacks import *"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uFPURKkfQATE",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#设置一个config类，便于参数配置与更改\n",
        "class Config(dict):\n",
        "    def __init__(self, **kwargs):\n",
        "        super().__init__(**kwargs)\n",
        "        for k, v in kwargs.items():\n",
        "            setattr(self, k, v)\n",
        "    \n",
        "    def set(self, key, val):\n",
        "        self[key] = val\n",
        "        setattr(self, key, val)\n",
        "\n",
        "config = Config(\n",
        "    testing=False,\n",
        "    bert_model_name=\"bert-base-chinese\", \n",
        "    #选用中文预训练模型：Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters\n",
        "    max_lr=3e-5,\n",
        "    epochs=1,\n",
        "    use_fp16=False, #fastai里可以方便地调整精度，加快训练速度：learner.to_fp16()\n",
        "    bs=8,\n",
        "    max_seq_len=128, #选取合适的seq_length，较大的值可能导致训练极慢报错等\n",
        ")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "3dS1_jNESiEv",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bYXEtepaSilw",
        "colab_type": "text"
      },
      "source": [
        "##Customize Tokenizer and Vocab"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "JiKPDWViStZS",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "3e7057eb-4b85-41a6-c79d-30c2fa61735a"
      },
      "source": [
        "#pytorch_bert包提供了BertTokenizer类，从选取的模型中提取tok\n",
        "from pytorch_pretrained_bert import BertTokenizer\n",
        "bert_tok = BertTokenizer.from_pretrained(\n",
        "    config.bert_model_name,\n",
        ")"
      ],
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "100%|██████████| 109540/109540 [00:00<00:00, 4157187.61B/s]\n"
          ],
          "name": "stderr"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8WHx6utjUCVs",
        "colab_type": "text"
      },
      "source": [
        "定制Tokenizer需要继承fastai的BaseTokenizer类，注意BERT会在sequence的首尾加上特殊token[CLS]和[SEP]，与fastai里的bos和eos有重合，所以在之后会有include_eos=Fasle的设置"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "09bYVoWBT0wr",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class FastAiBertTokenizer(BaseTokenizer):\n",
        "    \"\"\"Wrapper around a BertTokenizer to be a BaseTokenizer in fastai\"\"\"\n",
        "    def __init__(self, tokenizer: BertTokenizer, max_seq_len: int=128, **kwargs):\n",
        "        self._pretrained_tokenizer = tokenizer\n",
        "        self.max_seq_len = max_seq_len\n",
        "\n",
        "    def __call__(self, *args, **kwargs):\n",
        "        return self\n",
        "\n",
        "    def tokenizer(self, t:str) -> List[str]:\n",
        "        \"\"\"Limits the maximum sequence length\"\"\"\n",
        "        return [\"[CLS]\"] + self._pretrained_tokenizer.tokenize(t)[:self.max_seq_len - 2] + [\"[SEP]\"]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "0HD1qCGCVMMe",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#最终的tokenizer\n",
        "fastai_tokenizer = Tokenizer(\n",
        "    tok_func=FastAiBertTokenizer(bert_tok, max_seq_len=config.max_seq_len), \n",
        "    pre_rules=[], \n",
        "    post_rules=[]\n",
        ")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LxXk9yB1V0nB",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 369
        },
        "outputId": "cb24389b-849a-4396-e2af-2fc8b60b0475"
      },
      "source": [
        "#check the vocab\n",
        "list(bert_tok.vocab.items())[1090:1110]"
      ],
      "execution_count": 11,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[('冗', 1090),\n",
              " ('写', 1091),\n",
              " ('军', 1092),\n",
              " ('农', 1093),\n",
              " ('冠', 1094),\n",
              " ('冢', 1095),\n",
              " ('冤', 1096),\n",
              " ('冥', 1097),\n",
              " ('冨', 1098),\n",
              " ('冪', 1099),\n",
              " ('冬', 1100),\n",
              " ('冯', 1101),\n",
              " ('冰', 1102),\n",
              " ('冲', 1103),\n",
              " ('决', 1104),\n",
              " ('况', 1105),\n",
              " ('冶', 1106),\n",
              " ('冷', 1107),\n",
              " ('冻', 1108),\n",
              " ('冼', 1109)]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "NYIrjtkHW2F2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#设置vocab\n",
        "fastai_bert_vocab = Vocab(list(bert_tok.vocab.keys()))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DUU3lFXrXAUw",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KHfJODECXEVp",
        "colab_type": "text"
      },
      "source": [
        "##Dataset"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rwkcOhG3Uc_x",
        "colab_type": "text"
      },
      "source": [
        "本次使用的数据集信息：\n",
        "\n",
        "*   名称及出处：[ChnSentiCorp_htl_all](https://github.com/ZeroLeon/ChineseNlpCorpus/blob/master/datasets/ChnSentiCorp_htl_all/intro.ipynb)\n",
        "*   数据概览： 7000 多条酒店评论数据，5000 多条正向评论，2000 多条负向评论\n",
        "*   推荐实验： 情感/观点/评论 倾向性分析\n",
        "*   数据来源： 携程网\n",
        "*   原数据集： ChnSentiCorp_htl，由谭松波老师整理的一份数据集\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_4L8is6AXGB8",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#在colab下接入google drive，方便保存模型修改文件，使用其他kernel可跳过这一步\n",
        "from google.colab import drive\n",
        "drive.mount('/content/gdrive',force_remount = True)\n",
        "base_dir = '/content/gdrive/My Drive/fastai'\n",
        "path = Path(base_dir +'/data/htl_reviews')\n",
        "# path.mkdir(parents=True,exist_ok=True)\n",
        "os.chdir(path)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "5gU8EtWUXRjS",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!wget https://github.com/SophonPlus/ChineseNlpCorpus/raw/master/datasets/ChnSentiCorp_htl_all/ChnSentiCorp_htl_all.csv"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xk7iS5FpXngc",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "df_htl = pd.read_csv('ChnSentiCorp_htl_fix.csv',index_col=0)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "E7EE0A_hYS4E",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 343
        },
        "outputId": "33fdd402-59e3-4588-eca5-ffc0c0ad85df"
      },
      "source": [
        "#标签涵义：1代表正向评论，0代表负向评论\n",
        "label_denotation = {1:'positive',0:'negative'}\n",
        "df_htl.sample(10)"
      ],
      "execution_count": 39,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>label</th>\n",
              "      <th>review</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>803</th>\n",
              "      <td>1</td>\n",
              "      <td>房间能观景，很好~~主要是很方便。出门就是太平洋广场，很繁华的地段</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3675</th>\n",
              "      <td>1</td>\n",
              "      <td>酒店房间好，阳光明媚，卫生间大，可惜没享受。司机说，到拉萨第一天不要洗澡。房间空调足。好像早...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3539</th>\n",
              "      <td>1</td>\n",
              "      <td>房间很新，服务员水平也很高，物有所值。4楼房间太压抑，这次先进了4楼然后不满意很快换到10楼...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>6772</th>\n",
              "      <td>0</td>\n",
              "      <td>酒店实在太差了,房间隔音极差,北塔挨着迪吧，快曲放到半夜3点；换到南塔，又挨着KTV音乐响到...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2431</th>\n",
              "      <td>1</td>\n",
              "      <td>交通方便，对面就是国美电器。出入的车辆比较多，又刚好处于路口，所以有点混乱。5楼可以喝晚茶，...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>6511</th>\n",
              "      <td>0</td>\n",
              "      <td>Thepositionofthishotelisverybad.补充点评2008年4月6日：...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3634</th>\n",
              "      <td>1</td>\n",
              "      <td>酒店自动把房间升级成套房，不错，赞一下！但服务人员未免太自说自话，送水果也不能未经同意就自己...</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>5374</th>\n",
              "      <td>0</td>\n",
              "      <td>我感觉不行。。。性价比很差。不知道是银川都这样还是怎么的！</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1926</th>\n",
              "      <td>1</td>\n",
              "      <td>角房太冷了，tnnd。角房太冷了，tnnd。角房太冷了，tnnd。</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>7087</th>\n",
              "      <td>0</td>\n",
              "      <td>设施非常旧，周边环境也不好，最奇怪是房间内没有暖气，只有挂壁空调，要知道，这种空调室外-5度...</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "      label                                             review\n",
              "803       1                  房间能观景，很好~~主要是很方便。出门就是太平洋广场，很繁华的地段\n",
              "3675      1  酒店房间好，阳光明媚，卫生间大，可惜没享受。司机说，到拉萨第一天不要洗澡。房间空调足。好像早...\n",
              "3539      1  房间很新，服务员水平也很高，物有所值。4楼房间太压抑，这次先进了4楼然后不满意很快换到10楼...\n",
              "6772      0  酒店实在太差了,房间隔音极差,北塔挨着迪吧，快曲放到半夜3点；换到南塔，又挨着KTV音乐响到...\n",
              "2431      1  交通方便，对面就是国美电器。出入的车辆比较多，又刚好处于路口，所以有点混乱。5楼可以喝晚茶，...\n",
              "6511      0  Thepositionofthishotelisverybad.补充点评2008年4月6日：...\n",
              "3634      1  酒店自动把房间升级成套房，不错，赞一下！但服务人员未免太自说自话，送水果也不能未经同意就自己...\n",
              "5374      0                      我感觉不行。。。性价比很差。不知道是银川都这样还是怎么的！\n",
              "1926      1                  角房太冷了，tnnd。角房太冷了，tnnd。角房太冷了，tnnd。\n",
              "7087      0  设施非常旧，周边环境也不好，最奇怪是房间内没有暖气，只有挂壁空调，要知道，这种空调室外-5度..."
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 39
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LKBd5hlzYUkr",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#分割训练集train与验证集val\n",
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "train, val = train_test_split(df_htl)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "N69Q-7GjZzxq",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "45f15c1c-9bd8-4f1f-8af1-a729e78f3f8b"
      },
      "source": [
        "#check their size\n",
        "print(train.shape,val.shape)"
      ],
      "execution_count": 41,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "(5823, 2) (1942, 2)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "GWq0P4LnaL9M",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#建立TextDataBunch\n",
        "databunch = TextClasDataBunch.from_df(\".\", train, val,\n",
        "                  tokenizer=fastai_tokenizer,\n",
        "                  vocab=fastai_bert_vocab,\n",
        "                  include_bos=False,\n",
        "                  include_eos=False,\n",
        "                  text_cols=\"review\",\n",
        "                  label_cols='label',\n",
        "                  bs=config.bs,\n",
        "                  collate_fn=partial(pad_collate, pad_first=False, pad_idx=0),\n",
        "             )"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2EtfF2hrfFQw",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 279
        },
        "outputId": "c27831b3-a7c9-452b-dcfa-e469cd361f73"
      },
      "source": [
        "databunch.show_batch()"
      ],
      "execution_count": 43,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th>text</th>\n",
              "      <th>target</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <td>[CLS] 51 还 是 选 择 了 这 里 ， 因 为 贵 的 太 贵 ， 好 的 都 定 满 了 。 在 51 小 黄 金 288 元 的 价 格 是 属 于 便 宜 的 ， 之 前 打 电 话 去 游 记 中 有 人 写 到 的 枫 枫 旅 行 社 ， 一 个 私 人 开 的 小 旅 馆</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>[CLS] 洲 际 酒 店 ， 携 程 的 价 格 确 实 有 竞 争 力 。 应 该 是 沈 阳 最 便 宜 的 五 星 级 酒 店 。 1 ） 地 理 位 置 ： 位 于 商 业 街 太 原 街 附 近 ， 交 通 方 便 ， 但 门 口 老 是 堵 车 ， 建 议 打 车 的 话</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>[CLS] 这 次 到 莫 干 山 自 驾 游 ， 住 在 德 清 县 的 这 个 酒 店 里 。 先 说 说 好 的 方 面 ， 酒 店 的 地 理 位 置 不 错 ， 紧 邻 免 费 开 放 的 春 晖 公 园 。 开 车 到 莫 干 山 有 两 条 路 ， 大 约 40 分 钟 。</td>\n",
              "      <td>1</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>[CLS] 一 般 。 除 了 便 宜 。 一 个 大 问 题 就 是 浴 缸 的 设 计 有 问 题 ， 洗 澡 的 时 候 溅 出 的 水 会 顺 着 浴 缸 靠 墙 那 边 的 平 面 一 直 流 到 外 面 的 地 上 。 等 洗 完 之 后 ， 浴 室 里 已 经 是 水 漫</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>[CLS] 凌 晨 近 二 点 到 店 ， 就 我 一 个 客 人 在 前 台 办 手 续 ， 办 了 十 分 钟 才 办 好 ， 更 郁 闷 的 是 酒 店 竟 然 开 了 一 个 没 有 打 扫 过 的 房 间 给 我 ， 房 号 46 ##3 ， 住 了 这 么 多 年 的 酒 店 还</td>\n",
              "      <td>0</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>"
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "EWFDIeoJbgfb",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ""
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CZ4lw8TadPMS",
        "colab_type": "text"
      },
      "source": [
        "##Model and Loss Function"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6CLYlVWYdQlH",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "b58a66da-0dd6-4d17-e51d-ad16bd0cf1c1"
      },
      "source": [
        "from pytorch_pretrained_bert.modeling import BertConfig, BertForSequenceClassification\n",
        "bert_model = BertForSequenceClassification.from_pretrained(config.bert_model_name, num_labels=2)"
      ],
      "execution_count": 44,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "100%|██████████| 382072689/382072689 [00:07<00:00, 48731570.08B/s]\n"
          ],
          "name": "stderr"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xyaJ7ooIdhvq",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#二分类问题选用CrossEntrypyLoss作为损失函数\n",
        "loss_func = nn.CrossEntropyLoss()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "oN47kdZ-d69i",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#建立Learner(数据,预训练模型,损失函数)\n",
        "learner = Learner(databunch, bert_model,loss_func=loss_func,metrics=accuracy)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VhCBr5YyeYQz",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "cbf4de65-dc42-479e-f258-3d19af52a24e"
      },
      "source": [
        "#尝试寻找合适的最大学习率，这里使用了BERT原论文推荐的学习率3e-5作为默认值\n",
        "learner.lr_find()"
      ],
      "execution_count": 47,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              ""
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qj9hzFd0efEx",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 283
        },
        "outputId": "dee9db99-5783-400a-c20f-a33f098af923"
      },
      "source": [
        "learner.recorder.plot(skip_end=20)"
      ],
      "execution_count": 49,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\ngDfUYt4PBg8AAAAASUVORK5CYII=\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2C9Jwn7KemDB",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 77
        },
        "outputId": "a1fef34d-5ae6-4083-d81c-467015066ad0"
      },
      "source": [
        "#开始训练\n",
        "learner.fit_one_cycle(config.epochs, max_lr=config.max_lr)"
      ],
      "execution_count": 50,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: left;\">\n",
              "      <th>epoch</th>\n",
              "      <th>train_loss</th>\n",
              "      <th>valid_loss</th>\n",
              "      <th>accuracy</th>\n",
              "      <th>time</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <td>0</td>\n",
              "      <td>0.271018</td>\n",
              "      <td>0.230581</td>\n",
              "      <td>0.902163</td>\n",
              "      <td>04:43</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>"
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HUwZtDWCfZVz",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "learner.save(path/'htl_stage1')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VpOPW9I2g6EE",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "14049579-d2a1-4d55-d83b-918221c92436"
      },
      "source": [
        "#用样例测试下\n",
        "learner.predict(\"设施老化，紧靠马路噪音太大，晚上楼上卫生间的水流声和空调噪音非常大，无法入眠\")"
      ],
      "execution_count": 53,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "(Category 0, tensor(0), tensor([0.9943, 0.0057]))"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 53
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ODpG2dLcg5BG",
        "colab_type": "text"
      },
      "source": [
        "##What's Next?"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WgP6BrY3hGZH",
        "colab_type": "text"
      },
      "source": [
        "本次task属于二元文本分类，数据集并不大，同时有相当一部分数据属于正负向情感混合的文本，容易拉低准确度，因此结果表现没那么抢眼，只能略窥BERT的强大。下一步计划使用更大的数据集做中文文本多标签分类，输出模型制作成一个能自动给文本打上情感标签的应用。"
      ]
    }
  ]
}